<template>
  <div style="padding:10px;background-color: white;">
    <Row :space="10">
      <Cell width="24">
        <Row>
          <div class="container" :style="'height:' + height + 'px;'">
            <Markdown :source="result" />
          </div>
        </Row>
        <br />
        <Row>
          <Cell width="24">
            <div class="h-input-group">
              <Input type="text" noBorder placeholder="Enter发送" v-model="message" @keyup.enter="sendMessage"
                :loading="loadingSend" style="width:100%;background-color: #f5f4f47c;border: 1px solid #bdbaba;" />
              <Space :width="15" />
              <span :class="isLoading ? 'h-icon-loading' : 'link ri-send-plane-line'" @click="closeConnect"></span>
              <Space :width="15" />
              <span @click="clean" class="link ri-brush-3-line" title="清空对话"></span>
              <Space :width="15" />
              <span @click="openLogs" class="link h-icon-search" title="跟踪日志"></span>
              <Space :width="15" />
              <span @click="fileChange" class="link ri-upload-line" title="上传附件"></span>
              <Space :width="15" />
              <span class="link h-icon-setting" @click="opend = true" title="对话参数设置"></span>
            </div>
          </Cell>
        </Row>
      </Cell>
      <paas-upload-model url="/chat/upload"></paas-upload-model>
      <input v-show="false" :disabled="isLoading" ref="inputFile" type="file" @change="addFile($event)" />

      <Modal v-model="opend" :closeOnMask="true" :hasCloseIcon="true" :hasDivider="true" :draggable="true">
        <template #header>
          <Tabs :datas="tabData" v-model="selected"></Tabs>
        </template>
        <Form :labelWidth="90" style="width:600px">
          <div v-if="selected == 'chatConfig'">
            <FormItem label="对话模式">
              <Select v-model="chatType" :datas="chatTypeData" keyName="value" titleName="label"></Select>
            </FormItem>
            <FormItem label="输出设置">
              <Checkbox v-model="stream">流式输出</Checkbox>
              <Space :width="10" />
              <Checkbox v-model="cnAnswer">强制中文</Checkbox>
            </FormItem>
            <FormItem label="选择角色">
              <Select v-model="systemId" :datas="systemDatas" keyName="value" titleName="label"></Select>
            </FormItem>
            <FormItem label="系统提示词">
              <Input v-model="system" />
            </FormItem>
            <FormItem label="函数调用" v-if="chatType == '4'">
              <Select v-model="funcCall" :datas="funcs" keyName="value" titleName="label"></Select>
            </FormItem>
            <FormItem label="PPT模板" v-if="chatType == '4'">
              <Select v-model="pptTemplate" :datas="pptTemplates" keyName="value" titleName="label"></Select>
            </FormItem>
            <FormItem label="流程调用" v-if="chatType == '5'">
              <Select v-model="flowId" :datas="flows" keyName="value" titleName="label"></Select>
            </FormItem>
            <FormItem label="知识库集合" v-if="chatType == '2'">
              <Select v-model="collectionId" :datas="collectionList" @change="collectionChange"></Select>
            </FormItem>
            <FormItem label=" 知识库文件" v-if="chatType == '2'">
              <Select v-model="filename" :datas="filenameList" :title="filename"></Select>
            </FormItem>
            <FormItem label="最大条数" v-if="chatType == '2'">
              <NumberInput v-model="pageSize"></NumberInput>
            </FormItem>
            <FormItem label="相似比例" v-if="chatType == '2'">
              <Slider v-model="similarityScore" :range='{ start: 0, end: 10 }' :title="similarityScore" />
            </FormItem>
          </div>
          <div v-if="selected == 'modelConfig'">
            <FormItem label="设置模式">
              <Select v-model="mode" :datas="modes" keyName="value" titleName="label" @change="setMode"></Select>
            </FormItem>
            <FormItem label="选择模型">
              <Select v-model="model" :datas="models" keyName="value" titleName="label"></Select>
            </FormItem>
            <FormItem label="历史数量" single>
              <NumberInput v-model="cacheLimit"></NumberInput>
            </FormItem>
            <FormItem label="温度控制" title="控制输出结果的随机性：值越低更严谨、值越高创建性强。" single>
              <Slider v-model="temperature" :range='{ start: 0, end: 10 }'></Slider>
            </FormItem>
            <FormItem label="采样(P)" single title="累计概率达到的值，控制文本的随机性和多样性：表示选择token时的累积终止概率；值越低多样性低仅关注可用性更高的Token。">
              <Slider v-model="topP" :range='{ start: 0, end: 10 }'></Slider>
            </FormItem>
            <FormItem label="采样(K)" single title="token保留数量。选出排名前N个值">
              <Slider v-model="topK" :range='{ start: 0, end: 20 }'></Slider>
            </FormItem>
          </div>

        </Form>
      </Modal>
    </Row>

    <Modal v-model="logsOpend" :closeOnMask="false" :hasCloseIcon="false" :hasDivider="true" :draggable="true">
      <template #header>执行日志</template>
      <Textarea type="text" v-model="logs" readonly
        :style="'width:' + this.width * 0.5 + 'px;height:' + this.height * 1 + 'px'" />
      <template #footer>
        <Button color="yellow" @click="openLogs">清空</Button>
        <Space :width="10" />
        <Button @click="logsOpend = false">关闭</Button>
      </template>
    </Modal>
  </div>
</template>
<style>
.container {
  border: 1px solid #ccc;
  overflow: auto;
  padding: 30px;
}
</style>
<script>
import { fetchEventSource } from '@microsoft/fetch-event-source';
import { message } from 'heyui';
import 'highlight.js/styles/a11y-dark.css';
import { Base64 } from 'js-base64';
import Markdown from 'vue3-markdown-it';

export default {
  components: {
    Markdown
  },
  created() {
    this.getModel();
    this.getFlowsList();
    this.getSystemDatas();
    this.getFunctionsDatas();
    this.sessionId = this.generateSessionId();
    this.handleResize();
    this.getCollectionList();
    this.getPptTemplatesDatas();
  },
  mounted() {
    window.addEventListener('resize', this.handleResize)
  },
  data() {
    return {
      tabData: {
        chatConfig: '对话设置',
        modelConfig: '模型设置',
      },
      selected: 'chatConfig',
      chatTypeData: [
        { label: "文本对话", value: "1" },
        { label: "向量库对话", value: "2" },
        { label: "文件对话", value: "3" },
        { label: "工具对话", value: "4" },
        { label: "流程对话", value: "5" },
        { label: "图片对话", value: "6" },
        { label: "音频对话", value: "7" },
      ],
      opend: false,
      logsOpend: false,
      file: [],
      selectedFile: "",
      logs: "",
      socket: null,
      width: document.documentElement.clientWidth,
      height: document.documentElement.clientHeight,
      loadingSend: false,
      isLoading: false,
      models: [],
      flows: [],
      pptTemplates: [],
      source: [{ label: '模型对话', value: '1' }, { label: '向量对话', value: '2' }, { label: '文件对话', value: '3' }],
      modes: [{ label: '本地', value: '0' }, { label: '公司', value: '1' }],
      collectionList: [],
      systemDatas: [],
      funcs: [],
      filenameList: [],
      ctrl: new AbortController(),
      message: "",
      chatType: this.$store.chats.chatType,
      stream: this.$store.chats.stream,
      model: this.$store.chats.model,
      pptTemplate: this.$store.chats.pptTemplate,
      cacheLimit: this.$store.chats.cacheLimit,
      sourceType: this.$store.chats.sourceType,
      system: this.$store.chats.system,
      systemId: this.$store.chats.systemId,
      flowId: this.$store.chats.flowId,
      collectionId: this.$store.chats.collectionId,
      collectionName: this.$store.chats.collectionName,
      cnAnswer: this.$store.chats.cnAnswer,
      similarityScore: this.$store.chats.similarityScore,
      sessionId: this.$store.chats.sessionId,
      funcCall: this.$store.chats.funcCall,
      temperature: this.$store.chats.temperature,
      jsonFormat: this.$store.chats.jsonFormat,
      topK: this.$store.chats.topK,
      topP: this.$store.chats.topP,
      mode: this.$store.chats.mode,
      filename: this.$store.chats.filename,
      pageSize: this.$store.chats.pageSize,
      uploadFileId: this.$store.chats.uploadFileId,
      result: this.$store.chats.result,
    };
  },
  deactivated() {
    this.clean();
    this.$destroy(true);
    window.removeEventListener('resize', this.handleResize);
  },
  methods: {
    sendMessage() {
      if (window.EventSource) {
        if (!this.message) {
          return;
        }
        if (this.result != "") {
          this.result += "  \n  \n### " + this.message + "  \n";
        } else {
          this.result = '### ' + this.message + "  \n";
        }
        var sendStr = this.message;
        if (this.cnAnswer) {
          sendStr += ";使用中文回答"
        }
        if (this.pptTemplate) {
          sendStr = "使用模板“" + this.pptTemplate + "”创建PPT文档，" + sendStr
        }
        var sendData = {
          chatType: this.chatType,
          sessionId: this.sessionId,
          system: this.system,
          stream: this.stream,
          systemId: this.systemId,
          message: sendStr,
          model: this.model,
          cacheLimit: this.cacheLimit,
          uploadFileId: this.uploadFileId,
          sourceType: this.sourceType,
          collectionName: this.collectionName,
          cnAnswer: this.cnAnswer,
          collectionId: this.collectionId,
          jsonFormat: this.jsonFormat,
          funcCall: this.funcCall,
          similarityScore: this.similarityScore,
          temperature: this.temperature,
          result: this.result,
          flowId: this.flowId,
          topK: this.topK,
          topP: this.topP,
          filename: this.filename,
          mode: this.mode,
          pageSize: this.pageSize,
          pptTemplate: this.pptTemplate,
          filter: {
            filename: this.filename
          }
        }
        this.$store.setChats(sendData);
        var m = this;
        var data = Object.assign({ ...sendData }, {
          similarityScore: (this.similarityScore * 0.1).toFixed(2),
          temperature: (this.temperature * 0.1).toFixed(2),
          topK: (this.topK * 0.1).toFixed(2),
          topP: (this.topP * 0.1).toFixed(2),
        });
        var endData;
        var url = "";
        switch (this.chatType) {
          case "1":
            url = "/ollama/chat/text";
            break;
          case "2":
            url = "/ollama/chat/vector";
            break;
          case "3":
            url = "/ollama/chat/file";
            break;
          case "4":
            url = "/ollama/chat/tools";
            break;
          case "5":
            url = "/ollama/chat/flow";
            break;
          case "6":
            url = "/ollama/chat/image";
            break;
          case "7":
            message.info("功能开发中...")
            return;
        }
        fetchEventSource(url, {
          method: 'POST',
          headers: {
            'Content-Type': 'application/json',
          },
          openWhenHidden: true,
          body: JSON.stringify(data),
          signal: m.ctrl.signal,
          onopen: () => {
            m.isLoading = true;
            m.message = "";
          },
          onmessage: (e) => {
            if (this.isLoading) {
              endData = Base64.decode(e.data)
              m.$store.addResult(endData)
              m.result = m.$store.chats.result
            }
          },
          onclose() {
            m.isLoading = false;
            try {
              var jsonData = JSON.parse(endData);
              if (jsonData["actionType"] == "download") {
                m.$download("/chat/download/" + jsonData["filename"])
              }
            } catch (error) {
            }
            m.ctrl.abort();
          },
          onerror(err) {
            console.log(err);
          }
        })
      }
    },
    setMode() {
      this.$get("/chat/setMode?mode=" + this.mode).then(res => {
        if (res.data.code == this.$success) {
          this.logs = res.data.data;
          message.success(res.data.msg);
          this.getModel();
        } else {
          message.error(res.data.msg);
        }
      });
    },
    openLogs() {
      if (!this.logsOpend) {
        this.$get("/chat/getLogs").then(res => {
          if (res.data.code == this.$success) {
            this.logsOpend = true;
            this.logs = res.data.data;
          }
        });
      } else {
        this.logs = "";
        this.$get("/chat/cleanLogs").then(res => {
          if (res.data.code != this.$success) {
            message.error(res.data.msg);
          }
        });
      }
    },
    closeConnect() {
      if (!this.isLoading) {
        this.sendMessage();
      } else {
        this.$post("/chat/done/" + this.sessionId).then(res => {
          if (res.data.code == this.$success) {
            this.isLoading = false;
            this.ctrl.abort();
          }
        });
      }
    },
    getSystemDatas() {
      this.$post("/roles/list", { page: { current: 1, size: 1000 } }).then(res => {
        if (res.data.code == this.$success) {
          this.systemDatas = res.data.data.rows;
        }
      });
    },
    getFunctionsDatas() {
      this.$post("/ollamafunctions/list", { page: { current: 1, size: 1000 } }).then(res => {
        if (res.data.code == this.$success) {
          this.funcs = res.data.data.rows;
        }
      });
    },
    getPptTemplatesDatas() {
      this.$post("/ppttemplate/list", { page: { current: 1, size: 1000 } }).then(res => {
        if (res.data.code == this.$success) {
          this.pptTemplates = res.data.data.rows;
        }
      });
    },
    generateSessionId() {
      return 'id-' + Date.now();
    },
    clean() {
      //this.uploadFileId = "";
      this.isLoading = false;
      this.message = "";
      this.$store.setResult("")
      this.result = this.$store.chats.result
      this.$get("/chat/clean?sessionId=" + this.sessionId);
    },
    handleResize() {
      this.width = document.documentElement.clientWidth - 20;
      this.height = document.documentElement.clientHeight - 75;
    },
    getModel() {
      this.$get("/chat/getModel").then(res => {
        if (res.data.code == this.$success) {
          this.models = res.data.data;
        } else {
          message.error(res.data.msg);
        }
      });
    },

    sourceChange(data) {
      if (data.value == "2") {
        this.getCollectionList();
      }
    },
    getFlowsList() {
      this.$post("/fwconfig/list", { page: { current: 1, size: 1000 } }).then(res => {
        if (res.data.code == this.$success) {
          this.flows = res.data.data.rows;
        } else {
          this.flows = [];
          message.error(res.data.msg);
        }
      })
    },
    getFilenameList() {
      this.$post("/ollamaembedding/list", { vars: { "a.collection_id": this.collectionId }, page: { current: 1, size: 1000 } }).then(res => {
        if (res.data.code == this.$success) {
          this.filenameList = res.data.data.rows;
        } else {
          this.filenameList = [];
          message.error(res.data.msg);
        }
      })
    },
    getCollectionList() {
      this.$post("/ollamacollection/list", { page: { current: 1, size: 1000 } }).then(res => {
        if (res.data.code == this.$success) {
          this.collectionList = res.data.data.rows;
          if (!!this.collectionId) {
            this.getFilenameList();
          }
        } else {
          message.error(res.data.msg);
        }
      })
    },
    collectionChange(data) {
      this.filename = "";
      this.collectionName = data.label;
      this.getFilenameList(data.value);
    },
    fileChange() {
      this.$refs.inputFile.click();
    },
    addFile(event) {
      this.file = event.target.files;
      const names = [];
      for (let i = 0; i < this.file.length; i++) {
        names.push(this.file[i].name);
      }
      this.selectedFile = names.join(",");
      this.uploadFile(event);
    },
    uploadFile(event) {
      event.preventDefault();
      this.isLoading = true;
      const formData = new FormData();
      for (let i = 0; i < this.file.length; i++) {
        formData.append("file", this.file[i]);
      }
      if (this.params) {
        for (const key in this.params) {
          formData.append(key, this.params[key] || "");
        }
      }
      this.$upload("/chat/upload", formData).then(res => {
        this.isLoading = false;
        if (res.data.code == 200) {
          message.info("上传成功");
          this.file = [];
          console.log(res.data.data);
          this.uploadFileId = res.data.data;
        } else {
          message.error(res.data.msg);
        }
      }).catch(err => {
        this.isLoading = false;
        message.error(err);
      });
    },
  },
};
</script>